//////////////////////////////////////////////////////////////////////////////
//
//  Created by Philip Mulcahy on 30/01/2012.
//  Copyright (c) 2012 Philip Mulcahy. All rights reserved.
//
//  This file is part of the note-recog library.
//
//  note-recog is free software: you can redistribute it and/or modify
//  it under the terms of version 3 of the GNU Lesser General Public License 
//  as published by the Free Software Foundation.
//
//  note-recog is distributed is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with note-recog.  If not, see <http://www.gnu.org/licenses/>.
//
//////////////////////////////////////////////////////////////////////////////

#include <iostream>
#include "RecogImpl.h"
#include "IConsumer.h"
#include "NoteEvent.h"
#include "MutexLock.h"
#include <unistd.h>
#include <math.h>
#include "Listener.h"
#include <QuartzCore/CABase.h>
#include "Log.h"
#include <ctime>
#include <string>

using namespace std;

LOG_DEFINE(RecogImpl);

RecogImpl::RecogImpl(IConsumer * consumer, bool testScaleMode):
  _consumer( *consumer ),
  _testScaleMode(testScaleMode)
{
    LOG_INFO( "constructing" );
    
    pthread_mutex_init(&_mutex, NULL);
    
    _stopRequested = false;
    
    LOG_INFO( "constructed" );
}

void * threadRun(void * pArg);

void RecogImpl::start(){
    LOG_INFO( "start() starting" );
    
//    const time_t     now = time(0);
//    const struct tm  tstruct = *localtime(&now);
//    char             buf[80];
//    strftime(buf, sizeof(buf), "%Y-%m-%d.%X", &tstruct);
//    const std::string nowstring = buf;
//    const std::string limitstring = "2012-12-31.12:00:00";
//    
//    if( nowstring >= limitstring )
//    {
//        std::string msg = "";
//        msg += "V";
//        msg += "e";
//        msg += "r";
//        msg += "s";
//        msg += "i";
//        msg += "o";
//        msg += "n";
//        msg += " ";
//        msg += "i";
//        msg += "n";
//        msg += "v";
//        msg += "a";
//        msg += "l";
//        msg += "i";
//        msg += "d";
//        LOG_ERROR(msg);
//        return;
//    }
    
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
    
    if( _testScaleMode )
    {
        pthread_create(
                       &_thread, 
                       &attr,
                       threadRun, 
                       static_cast<void *>(this)
                       );
    }
    else
    {
        Listener::instance().start(&_consumer);
    }
    
    LOG_INFO( "start() complete" );
}

bool RecogImpl::getStopRequested() const
{
    MutexLock lock(_mutex);
    return _stopRequested;
}

void * threadRun(void * pArg)
{
    RecogImpl * const recog = 
    static_cast<RecogImpl *>(pArg);
    
    int noteNum = 0;
    
    while(!recog->getStopRequested())
    {
        const float freq = 
        440.0f * pow( 2.0f, static_cast<float>(noteNum) / 12.0f ); 
        
        recog->doCycle(freq);
        
        usleep(500000);
        
        ++noteNum;
        noteNum %= 13;
    }
    
    pthread_exit(NULL);
}

RecogImpl::~RecogImpl()
{
    LOG_INFO( "destroying RecogImpl" );
    
    {
        MutexLock lock(_mutex);
        _stopRequested = true;
    }
    
    if( _testScaleMode )
    {
        void *status;
        pthread_join(_thread,&status);
    }
    else
    {
        Listener::instance().stop();
    }
    
    {
        MutexLock lock(_mutex);
        _stopRequested = false;
    }
    
    pthread_mutex_destroy(&_mutex);
    
    LOG_INFO( "destroyed RecogImpl" );
}

void RecogImpl::doCycle(float freq)
{
    LOG_INFO( "doCycle(" << freq << ") starting" );
    
    const NoteEvent evt(CACurrentMediaTime(),freq);
    _consumer.onNoteRecognized(evt);
    
    LOG_INFO( "doCycle(" << freq << ") finished" );
}
